﻿using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;

using Pegasus.Diagnostics;
using Pegasus.Log4Net;

namespace Pegasus.Globalization
{
	/// <summary>
	/// 
	/// </summary>
	public class ResourceManager
	{
		// Local Instance Values
		private ILog m_log = LogManager.GetLogger( typeof( ResourceManager ) );
		private Hashtable m_valuesTable = new Hashtable();
		private CultureInfo m_culture;
		private bool m_default = false;

		/// <summary>
		/// 
		/// </summary>
		internal ResourceManager( Assembly assembly, string resourceName )
		{
			// Check params
			ParamCode.AssertNotNull( assembly, "assembly" );
			ParamCode.AssertNotEmpty( resourceName, "resourceName" );

			LoadResoucesFromAssembly( assembly, resourceName );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <value></value>
		public CultureInfo Culture
		{
			get
			{
				return m_culture;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <value></value>
		public bool Default
		{
			get
			{
				return m_default;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public string this[ string key ]
		{
			get
			{
				ParamCode.AssertNotEmpty( key, "key" );
				return (string) GetObject( key );
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public object GetObject( string key )
		{
			// Check params
			ParamCode.AssertNotEmpty( key, "key" );
			return m_valuesTable[ key ];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <param name="args"></param>
		/// <returns></returns>
		public string GetString( string key, params object[] args )
		{
			// Check params
			ParamCode.AssertNotEmpty( key, "key" );
			return string.Format( (string) GetObject( key ), args );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public int GetInt( string key )
		{
			// Check params
			ParamCode.AssertNotEmpty( key, "key" );
			return (int) GetObject( key );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public ArrayList GetArrayList( string key )
		{
			// Check params
			ParamCode.AssertNotEmpty( key, "key" );
			return (ArrayList) GetObject( key );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public Hashtable GetHashtable( string key )
		{
			// Check params
			ParamCode.AssertNotEmpty( key, "key" );
			return (Hashtable) GetObject( key );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="form"></param>
		public void ApplyResoucesToForm( System.Windows.Forms.Form form )
		{
			// Check params
			ParamCode.AssertNotNull( form, "form" );
			ResourceForms.ApplyResoucesToForms( this, form );
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="assembly"></param>
		/// <param name="resourceName"></param>
		private void LoadResoucesFromAssembly( Assembly assembly, string resourceName )
		{
			// Check params
			ParamCode.AssertNotNull( assembly, "assembly" );
			ParamCode.AssertNotEmpty( resourceName, "resouceName" );

			Stream resStream = null;
			try
			{
				resStream = assembly.GetManifestResourceStream( resourceName );
//				DebugCode.AssertNotNull( resStream, "Resource '{0}' was not found in assembly '{1}'", resourceName, assembly.FullName );

				XmlDocument xmlDoc = new XmlDocument();
				xmlDoc.Load( resStream );

				XmlNodeList resNodes = xmlDoc.GetElementsByTagName( "resources" );
//				DebugCode.Assert( resNodes.Count > 0, "No <resource> tags found in xml file" );
				if( resNodes.Count > 0 )
				{
					// Process the resouce node
					XmlNode resNode = resNodes[ 0 ];

					XmlAttribute cultureAttrib = resNode.Attributes[ "culture" ];
					if( cultureAttrib == null )
					{
						throw new Exception( "Resource element missing culture attribute" );
					}

					m_culture = new CultureInfo( cultureAttrib.Value );

					XmlAttribute defaultAttrib = resNode.Attributes[ "default" ];
					if( defaultAttrib != null )
					{
						m_default = Convert.ToBoolean( defaultAttrib.Value );
					}

					// Process the child node for the resources
//					DebugCode.Assert( resNodes[ 0 ].ChildNodes.Count > 0, "No child elements listed in <resource> tags" );
					foreach( XmlNode node in resNodes[ 0 ].ChildNodes )
					{
						object oldValue = m_valuesTable[ node.Name ];
						m_valuesTable[ node.Name ] = ProcessNode( node, oldValue );
					}
				}
			}
			finally
			{
				if( resStream != null )
				{
					resStream.Close();
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="node"></param>
		/// <param name="oldObject"></param>
		/// <returns></returns>
		private object ProcessNode( XmlNode node, object oldObject )
		{
			// Check params
			ParamCode.AssertNotNull( node, "node" );
			// oldValue: may or may not have value

			string type = "string";

			// If this is a comment node the just return
			if( node.Name == "#comment" )
			{
				return null;
			}

			if( node.Attributes != null )
			{
				XmlAttribute attrib = node.Attributes[ "type" ];
				if( attrib != null )
				{
					type = attrib.Value;
				}
			}

			switch( type.ToLower() )
			{
				case "arraylist":
					return AddArrayList( node, (ArrayList) oldObject );

				case "hashtable":
					return AddHashtable( node, (Hashtable) oldObject );

				case "int":
				case "int32":
					return Convert.ToInt32( node.InnerText );

				case "string":
				default:
					return node.InnerText.Trim();
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="node"></param>
		/// <param name="list"></param>
		/// <returns></returns>
		private object AddArrayList( XmlNode node, ArrayList list )
		{
			// Check params
			ParamCode.AssertNotNull( node, "node" );
			// list may or may not have value

			string name = node.Name;
			if( list == null )
			{
				list = new ArrayList();
			}

			foreach( XmlNode childNode in node.SelectNodes( "value" ) )
			{
				list.Add( ProcessNode( childNode, null ) );
			}

			return list;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="node"></param>
		/// <param name="table"></param>
		/// <returns></returns>
		private object AddHashtable( XmlNode node, Hashtable table )
		{
			// Check params
			ParamCode.AssertNotNull( node, "node" );
			// table may or may not have value

			string name = node.Name;
			if( table == null )
			{
				table = new Hashtable();
			}

			foreach( XmlNode childNode in node.ChildNodes )
			{
				object oldValue = table[ node.Name ];
				table[ childNode.Name ] = ProcessNode( childNode, oldValue );
			}

			return table;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="assembly"></param>
		/// <param name="resourceName"></param>
		/// <param name="culture"></param>
		/// <returns></returns>
		public static ResourceManager GetResouceManager( Assembly assembly, string resourceName, CultureInfo culture )
		{
			// Check params
			ParamCode.AssertNotNull( assembly, "assembly" );
			ParamCode.AssertNotEmpty( resourceName, "resourceName" );
			ParamCode.AssertNotNull( culture, "culture" );

			ResourceCache resCache = new ResourceCache( assembly, resourceName );
			return resCache.GetResourceManager( culture );
		}
	}
}
